#!/usr/bin/env python
# -*- coding: utf-8 -*-

import jinja2
import datetime

from farado.logger import logger
from farado.general_manager_holder import gm_holder
from farado.items.field_kind import ValueTypes
from farado.database.raw_querier import unite, intersect
from farado.helpers.long_action_watcher import LongActionHandler


class IssuesColumnHelper(LongActionHandler):

    def __init__(self, issue=None) -> None:
        if not issue:
            return
        self.issue = issue
        self.issue_kind = gm_holder.project_manager.issue_kind(
            issue.issue_kind_id
        )
        self.workflow = gm_holder.project_manager.workflow(
            self.issue_kind.workflow_id
        )
        self.state = gm_holder.project_manager.state(issue.state_id)
        self.parent_issue = gm_holder.project_manager.issue(issue.parent_id)
        self.project = gm_holder.project_manager.project(issue.project_id)
        self.version = gm_holder.project_manager.version(issue.version_id)

    #--------------------------------------------------------------------------#
    # TODO Учесть переводы в методе
    def caption(self, column):
        if 'id' == column:
            return '№'

        if 'kind' == column:
            return 'Тип'

        if 'state' == column:
            return 'Состояние'

        if 'caption' == column:
            return 'Наименование'

        if 'parent' == column:
            return 'Предок'

        if 'project' == column:
            return 'Проект'

        if 'version' == column:
            return 'Версия'

        if 'created' == column:
            return 'Создано'

        if 'create_user' == column:
            return 'Создал'

        if 'last_modified' == column:
            return 'Изменено'

        if 'last_modify_user' == column:
            return 'Изменил'

        if 'field_kind' in column:
            result = []
            for field_kind_column_part in column.split('|'):
                field_kind_id = int(field_kind_column_part.split('_')[-1])
                field_kind = gm_holder.project_manager.field_kind(
                    field_kind_id)
                if not field_kind:
                    continue
                if field_kind.caption in result:
                    continue
                result.append(field_kind.caption)
            return ', '.join(str(item) for item in result)

        return '—'

    #--------------------------------------------------------------------------#
    def js_render_function(self, column):
        if 'id' == column:
            return js_render_id_template.render()

        if 'kind' == column:
            return js_render_kind_template.render()

        if 'state' == column:
            return js_render_state_template.render()

        if 'caption' == column:
            return js_render_caption_template.render()

        if 'parent' == column:
            return js_render_parent_template.render()

        if 'project' == column:
            return js_render_project_template.render()

        if 'version' == column:
            return js_render_version_template.render()

        if 'field_kind' in column:
            return js_render_field_kind_template.render(value_types=ValueTypes)

        if 'create_user' == column:
            return js_render_create_user_template.render()

        if 'last_modify_user' == column:
            return js_render_last_modify_user_template.render()

        if 'created' == column:
            return js_render_created_template.render()

        if 'last_modified' == column:
            return js_render_last_modified_template.render()

        return 'function(data, type, row) { return ""; }'

    #--------------------------------------------------------------------------#
    def is_orderable_column(self, column):
        # TODO: В составных столбцах временно не работает сортировка
        if '|' in column:
            return False
        if 'id' == column:
            return True
        if 'kind' == column:
            return True
        if 'state' == column:
            return True
        if 'caption' == column:
            return True
        if 'parent' == column:
            return True
        if 'project' == column:
            return True
        if 'version' == column:
            return True
        if 'field_kind' in column:
            return True
        if 'created' in column:
            return True
        if 'create_user' in column:
            return False
        if 'last_modified' in column:
            return True
        if 'last_modify_user' in column:
            return False
        return False

    #--------------------------------------------------------------------------#
    def is_searchable_column(self, column):
        if 'id' == column:
            return True
        if 'kind' == column:
            return True
        if 'state' == column:
            return True
        if 'caption' == column:
            return True
        if 'parent' == column:
            return True
        if 'project' == column:
            return True
        if 'version' == column:
            return True
        if 'field_kind' in column:
            return True
        if 'created' in column:
            return True
        if 'create_user' in column:
            return False
        if 'last_modified' in column:
            return True
        if 'last_modify_user' in column:
            return False
        return False

    #--------------------------------------------------------------------------#
    def prepare_column_data(self, column):
        if 'id' == column.data:
            return {column.data: self.issue.id}

        if 'kind' == column.data:
            return {
                column.data: {
                    'id': self.issue.issue_kind_id,
                    'caption': self.issue_kind.caption if self.issue_kind else '—',
                    'workflow': self.workflow.caption if self.workflow else '—',
                }
            }

        if 'state' == column.data:
            return {
                column.data: {
                    "id": self.issue.state_id,
                    "caption": self.state.caption if self.state else '—',
                    "progress": gm_holder.project_manager.progress_value(self.issue.id),
                }
            }

        if 'caption' == column.data:
            return {
                column.data: {
                    'id': self.issue.id,
                    'caption': self.issue.caption,
                    'parent_id': self.parent_issue.id if self.parent_issue else '',
                    'parent': self.parent_issue.caption if self.parent_issue else '',
                }
            }

        if 'parent' == column.data:
            return {
                column.data: [
                    self.issue.parent_id,
                    self.parent_issue.caption if self.parent_issue else '—'
                ]
            }

        if 'project' == column.data:
            return {
                column.data: [
                    self.issue.project_id,
                    self.project.caption if self.project else "—"
                ]
            }

        if 'version' == column.data:
            return {
                column.data: [
                    self.issue.version_id,
                    self.version.caption if self.version else "—"
                ]
            }

        if 'field_kind' in column.data:
            return self.prepare_field_kind_column_data(column)

        if 'created' == column.data:
            return {column.data: self.issue.created()}

        if 'create_user' == column.data:
            user_id = self.issue.create_user_id()
            user = gm_holder.project_manager.user(user_id)
            return {column.data: [user_id, f'{user.last_name} {user.first_name}' if user else ""]}

        if 'last_modified' == column.data:
            return {column.data: self.issue.last_modified()}

        if 'last_modify_user' == column.data:
            user_id = self.issue.last_modify_user_id()
            user = gm_holder.project_manager.user(user_id)
            return {column.data: [user_id, f'{user.last_name} {user.first_name}' if user else ""]}

        return {column.data: False}

    #--------------------------------------------------------------------------#
    def prepare_field_kind_column_data(self, column):
        ''' Пример:
            {
                "field_kind_1|field_kind_2":
                [
                    {
                        "id": 1,
                        "raw_value": 2,
                        "value": "text",
                        "value_type": 5,
                        "filter_input": "field_kind_1_field_kind_2"
                    },
                    {}
                ]
            }
        '''
        result = []
        for field_kind_column_part in column.data.split('|'):
            field_kind_id = int(field_kind_column_part.split('_')[-1])
            field = self.issue.field(field_kind_id=field_kind_id)
            field_kind = gm_holder.project_manager.field_kind(
                field_kind_id=field_kind_id
            )
            if not field or not field_kind:
                result.append({})
                continue

            result_instance = {
                'id': field.id,
                'raw_value': field.value,
                'value': field.value,
                'value_type': field_kind.value_type,
                'filter_input': column.data.replace('|', '_'),
            }

            if ValueTypes.date_time == field_kind.value_type:
                value = ''
                if field.value:
                    value = f'{datetime.datetime.fromisoformat(field.value):%d.%m.%Y %H:%M:%S}'
                result_instance['value'] = value

            elif ValueTypes.issue_id == field_kind.value_type:
                issue = gm_holder.project_manager.issue(field.value)
                result_instance['value'] = issue.caption if issue else ""

            elif ValueTypes.user_id == field_kind.value_type:
                user = gm_holder.project_manager.user(field.value)
                result_instance['value'] = f'{user.last_name} {user.first_name}' if user else ""

            elif ValueTypes.project_id == field_kind.value_type:
                project = gm_holder.project_manager.project(field.value)
                result_instance['value'] = project.caption if project else ""

            elif ValueTypes.version_id == field_kind.value_type:
                version = gm_holder.project_manager.version(field.value)
                result_instance['value'] = version.caption if version else ""

            # TODO Охватить issues_ids, users_ids и projects_ids

            result.append(result_instance)
        return {column.data: result}

    #--------------------------------------------------------------------------#
    def filter_data(self, columns):
        '''Формирует фильтры-условия по заданным столбцам для дальнейшего
        запроса в БД.

        Параметры
        ----------
        columns : list<DataTableArgs::Column>
            Контейнер данных по столбцам, с информацией по каким критериям
            выполнять фильтрацию.

        Возвращает
        -------
        caption_contains : string
            Сторка, наличие который предполагается проверить в наименовании
            Issue.

        issue_kinds_ids : list<int>
            Перечень идентификаторов объектов типа IssueKind, по которым
            предполагается выполнение фильтрации итогового набора Issue.

        states_ids : list<int>
            Перечень идентификаторов объектов типа State, по которым
            предполагается выполнение фильтрации итогового набора Issue.

        parents_ids : list<int>
            Перечень идентификаторов родительских Issue к целевым, по которым
            предполагается выполнение фильтрации итогового набора Issue.

        projects_ids : list<int>
            Перечень идентификаторов проектов, по которым
            предполагается выполнение фильтрации итогового набора Issue.

        versions_ids : list<int>
            Перечень идентификаторов версий, по которым
            предполагается выполнение фильтрации итогового набора Issue.

        field_contains : dict<int, string>
            Словарь перечня полей Issue и значений, наличие которых
            предполагается проверить, где ключ — FieldKind.id, значение —
            искомая строка или контейнер искомых идентификаторов.
        '''
        # Возвращаемые поля
        caption_contains = None

        issue_kinds_ids = []
        need_search_in_kinds = False

        states_ids = []
        need_search_in_states = False

        parents_ids = []

        projects_ids = []
        need_search_in_projects = False

        versions_ids = []
        need_search_in_versions = False

        field_contains = {}

        for column in columns:
            # Фильтр по наименованию issue
            if 'caption' == column.data:
                caption_contains = column.search_value

            # Фильтр по наименованию issue_kind
            elif 'kind' == column.data and bool(column.search_value):
                need_search_in_kinds = True
                for issue_kind in gm_holder.project_manager.issue_kinds():
                    if column.search_value in issue_kind.caption:
                        issue_kinds_ids.append(issue_kind.id)

            # Фильтр по наименованию state
            elif 'state' == column.data and bool(column.search_value):
                need_search_in_states = True
                for state in gm_holder.project_manager.states():
                    if column.search_value in state.caption:
                        states_ids.append(state.id)

            # Фильтр по наименованию родительских issue
            elif 'parent' == column.data and bool(column.search_value):
                pass  # TODO реализовать поиск по родительскому issue

            # Фильтр по наименованию проекта
            elif 'project' == column.data and bool(column.search_value):
                need_search_in_projects = True
                for project in gm_holder.project_manager.projects():
                    if column.search_value in project.caption:
                        projects_ids.append(project.id)

            # Фильтр по наименованию версии
            elif 'version' == column.data and bool(column.search_value):
                need_search_in_versions = True
                for version in gm_holder.project_manager.versions():
                    if column.search_value in version.caption:
                        versions_ids.append(version.id)

            # Фильтр по значению полей issue
            elif 'field_kind' in column.data and bool(column.search_value):
                field_kind_id = int(column.data.split('_')[-1])
                field_kind = gm_holder.project_manager.field_kind(
                    field_kind_id=field_kind_id
                )

                # Фильтр по типу поля issue_id
                if ValueTypes.issue_id == field_kind.value_type:
                    # TODO реализовать поиск по issue на котороре ссылается поле
                    field_contains[field_kind_id] = column.search_value

                # Фильтр по типу поля user_id
                elif ValueTypes.user_id == field_kind.value_type:
                    field_contains[field_kind_id] = []
                    for user in gm_holder.project_manager.users():
                        if column.search_value in f'{user.last_name} {user.first_name}':
                            field_contains[field_kind_id].append(user.id)
                    if not field_contains[field_kind_id]:
                        field_contains[field_kind_id] = [None]

                # Фильтр по типу поля project_id
                elif ValueTypes.project_id == field_kind.value_type:
                    field_contains[field_kind_id] = []
                    for project in gm_holder.project_manager.projects():
                        if column.search_value in project.caption:
                            field_contains[field_kind_id].append(project.id)
                    if not field_contains[field_kind_id]:
                        field_contains[field_kind_id] = [None]

                # Фильтр по типу поля version_id
                elif ValueTypes.version_id == field_kind.value_type:
                    field_contains[field_kind_id] = []
                    for version in gm_holder.project_manager.versions():
                        if column.search_value in version.caption:
                            field_contains[field_kind_id].append(version.id)
                    if not field_contains[field_kind_id]:
                        field_contains[field_kind_id] = [None]

                # Прочие поля фильтруются непосредственно по значению поля
                else:
                    field_contains[field_kind_id] = column.search_value

        # Заполнение невалидными данными в случа неудачного поиска
        if need_search_in_kinds and not issue_kinds_ids:
            issue_kinds_ids = [None]

        if need_search_in_states and not states_ids:
            states_ids = [None]

        if need_search_in_projects and not projects_ids:
            projects_ids = [None]

        if need_search_in_versions and not versions_ids:
            versions_ids = [None]

        return (
            caption_contains,
            issue_kinds_ids,
            states_ids,
            parents_ids,
            projects_ids,
            versions_ids,
            field_contains,
        )

    def issues_ids_by_data_table_args(self, table_args):
        '''Формирует перечень идентификаторов экземпляров Issue по данным
        из объекта DataTableArgs (фильтры и сортировки результирующей таблицы).

        FIXME: метод не оптимизирован и может выполняться достаточно долго! 

        Параметры
        ----------
        table_args : DataTableArgs
            Контейнер данных по столбцам, с информацией по каким критериям
            выполнять фильтрацию.

        Возвращает
        -------
        issues_ids : list<int>
            Перечень идентификаторов объектов типа Issue.
        '''
        result = None
        order_column_ids = None
        raw_querier = gm_holder.meta_item_manager.raw_querier

        for column in table_args.columns:
            if self.is_stopped:
                return []

            is_order = bool(column.number == table_args.order_column)
            is_order_ascending = table_args.is_order_ascending if is_order else None
            sub_result = None

            # Фильтр по идентификатору issue
            if 'id' == column.data:
                sub_result = raw_querier.issues_ids_by_id(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию issue
            elif 'caption' == column.data:
                sub_result = raw_querier.issues_ids_by_caption(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию issue_kind
            elif 'kind' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_kind(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию state
            elif 'state' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_state(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию родительских issue
            elif 'parent' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_parent(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию проекта
            elif 'project' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_project(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по наименованию версии
            elif 'version' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_version(
                    value=column.search_value,
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по значению полей issue
            elif 'field_kind' in column.data and (column.search_value or is_order):
                sub_result = None
                for field_kind_column_part in column.data.split('|'):
                    field_kind_id = int(field_kind_column_part.split('_')[-1])
                    field_kind = gm_holder.project_manager.field_kind(
                        field_kind_id=field_kind_id
                    )
                    if not field_kind:
                        continue
                    ids = raw_querier.issues_ids_by_field(
                        field_kind_id=field_kind_id,
                        value_type=field_kind.value_type,
                        value=column.search_value,
                        is_order_asc=is_order_ascending,
                    )
                    if sub_result is None:
                        sub_result = ids
                    elif "!" == column.search_value:
                        sub_result = intersect(ids, sub_result)
                    else:
                        sub_result = unite(ids, sub_result)

            # Фильтр по дате/времени создания запроса
            elif 'created' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_created(
                    value=date_to_iso(column.search_value),
                    is_order_asc=is_order_ascending,
                )

            # Фильтр по дате/времени последнего изменения запроса
            elif 'last_modified' == column.data and (column.search_value or is_order):
                sub_result = raw_querier.issues_ids_by_last_modified(
                    value=date_to_iso(column.search_value),
                    is_order_asc=is_order_ascending,
                )

            if sub_result is None:
                continue

            if is_order:
                order_column_ids = sub_result
            elif result is None:
                result = sub_result
            else:
                result = intersect(sub_result, result)

        if result is None:
            result = order_column_ids
        elif not order_column_ids is None:
            result = intersect(order_column_ids, result)

        return result

#------------------------------------------------------------------------------#


js_result_header = '''
{% macro result_header(
    parent,
    parent_link)
-%}
    {%- if parent_link and parent -%}
        + '<a href="{{ parent_link }}" class="parent-title link" title="'
        + {{ parent }} + '">'
            + {{ parent }}
        + '</a>'
    {%- elif parent -%}
        + '<span class="parent-title link" title="'
        + {{ parent }} + '">'
            + {{ parent }}
        + '</span>'
    {%- endif -%}
{%- endmacro %}
'''

js_result_caption_link = '''
{% macro result_caption_link(
    caption,
    link='')
-%}
    {%- if link -%}
        + '<a href="{{ link }}" class="link" title="'
        + {{ caption }} + '">'
            + {{ caption }}
        + '</a>'
    {%- else -%}
        + {{ caption }}
    {%- endif -%}
{%- endmacro %}
'''

js_result_buttons = '''
{% macro result_buttons(
    caption,
    filter='')
-%}
    + ' <span class="hidden-child ms-2">'
        {%- if filter -%}
            + '<a href="#" class="me-1"'
            + ' title="Фильтр содержит «' + {{ caption }} + '»"'
            + ' onclick="filter_field(\\'{{ filter }}\\', \\''
            + {{ caption }} + '\\' );">'
                + '<i class="bi bi-funnel-fill size-icon"></i>'
            + '</a>'
            + '<a href="#"'
            + ' title="Фильтр не содержит «' + {{ caption }} + '»"'
            + ' onclick="filter_field(\\'{{ filter }}\\', \\'!'
            + {{ caption }} + '\\' );">'
                + '<i class="bi bi-funnel size-icon"></i>'
            + '</a>'
        {%- endif -%}
    + '</span>'
{%- endmacro %}
'''

js_result_progress = '''
{% macro result_progress(
    progress)
-%}
    {%- if progress -%}
        + '<div class="col-12">'
            + '<div class="progress" style="height: 2px;"'
                + ' title="' + {{ progress }} + '%">'
                + '<div'
                    + ' class="progress-bar'
                    + ( (100 === {{ progress }})
                        ? ' bg-success'
                        : ( (40 > {{ progress }})
                            ? ' bg-warning'
                            : '')
                    ) + '"'
                    + ' role="progressbar"'
                    + ' style="width: ' + {{ progress }} + '%;"'
                    + ' aria-valuenow="' + {{ progress }} + '"'
                    + ' aria-valuemin="0"'
                    + ' aria-valuemax="100"></div>'
            + '</div>'
        + '</div>'
    {%- endif -%}
{%- endmacro %}
'''

js_result_block = (
    js_result_header +
    js_result_caption_link +
    js_result_buttons +
    js_result_progress +
'''
{% macro result(
    caption,
    link='',
    filter='',
    parent='',
    parent_link='',
    progress='')
-%}
    '<div class="table-cell-value d-flex align-items-start flex-column justify-content-start">'
        {#- Заголовок ячейки-#}
        {{- result_header(parent, parent_link) -}}

        {#- Основной контент ячейки -#}
        + '<div class="d-flex align-items-center justify-content-between">'

            {#- Текст контента ячейки или ссылка -#}
            {{- result_caption_link(caption, link) -}}

            {#- Элементы управления ячейки -#}
            {{- result_buttons(caption, filter) -}}

        + '</div>'

        {#- Прогресс -#}
        {{- result_progress(progress) -}}

    + '</div>';
{%- endmacro %}
''')


js_render_id_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    return {{ result(
        caption = "data",
        filter = "id")
    }}
}
'''

js_render_kind_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data.id == undefined) {
        return;
    }
    return {{ result(
        caption = "escapeHtml(data.caption)",
        filter = "kind",
        parent = "escapeHtml(data.workflow)")
    }}
}
'''

js_render_state_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data.id == undefined) {
        return;
    }
    return {{ result(
        caption = "escapeHtml(data.caption)",
        filter = "state",
        progress = "data.progress")
    }}
}
'''

js_render_caption_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data.id == undefined) {
        return '';
    }
    return {{ result(
        caption = "escapeHtml(data.caption)",
        link = "/issues/issue/' + data.id + '",
        filter = "caption",
        parent = "escapeHtml(data.parent)",
        parent_link = "/issues/issue/' + data.parent_id + '",
        )
    }}
}
'''

js_render_parent_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data[0] === null) {
        return '';
    }
    return {{ result(
        caption = "escapeHtml(data[1])",
        link = "/issues/issue/' + data[0] + '",
        filter = "parent")
    }}
}
'''

js_render_project_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data[0] === null) {
        return '';
    }
    return {{ result(
        caption = "escapeHtml(data[1])",
        link = "/projects/project/' + data[0] + '",
        filter = "project")
    }}
}
'''

js_render_version_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data[0] === null) {
        return '';
    }
    return {{ result(
        caption = "escapeHtml(data[1])",
        link = "/projects/version/' + data[0] + '",
        filter = "version")
    }}
}
'''

js_render_field_kind_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (!data instanceof Array) {
        return '';
    }
    result = "";
    data.forEach((item, index) => {
        if (item.value_type == undefined) {
            return;
        }
        switch (item.value_type) {
            case {{ value_types.bool|int }}:
                {# TODO: фильтрация по типу bool не работает #}
                result += {{ result(
                    caption = "String(item.value ? 'Вкл' : 'Откл')" )
                }}
                break;
            case {{ value_types.uri|int }}:
                result += {{ result(
                    caption = "escapeHtml(item.value)",
                    link = "' + item.value + '",
                    filter = "' + item.filter_input + '")
                }}
                break;
            case {{ value_types.issue_id|int }}:
                result += {{ result(
                    caption = "escapeHtml(item.value)",
                    link = "/issues/issue/' + item.raw_value + '",
                    filter = "' + item.filter_input + '")
                }}
                break;
            case {{ value_types.user_id|int }}:
                result += {{ result(
                    caption = "escapeHtml(item.value)",
                    link = "/users/user_statistics/' + item.raw_value + '",
                    filter = "' + item.filter_input + '")
                }}
                break;
            case {{ value_types.project_id|int }}:
                result += {{ result(
                    caption = "escapeHtml(item.value)",
                    link = "/projects/project/' + item.raw_value + '",
                    filter = "' + item.filter_input + '")
                }}
                break;
            case {{ value_types.version_id|int }}:
                result += {{ result(
                    caption = "escapeHtml(item.value)",
                    link = "/projects/version/' + item.raw_value + '",
                    filter = "' + item.filter_input + '")
                }}
                break;
            default:
                result += {{ result(
                    caption = "item.value",
                    filter = "' + item.filter_input + '")
                }}
                break;
        }
    });
    return result;
}
'''

js_render_create_user_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data[0] === null) {
        return '';
    }
    {# TODO: добавить filter = "create_user" когда заработает фильтр #}
    return {{ result(
        caption = "escapeHtml(data[1])",
        link = "/users/user_statistics/' + data[0] + '")
    }}
}
'''

js_render_last_modify_user_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    if (data[0] === null) {
        return '';
    }
    {# TODO: добавить filter = "last_modify_user" когда заработает фильтр #}
    return {{ result(
        caption = "escapeHtml(data[1])",
        link = "/users/user_statistics/' + data[0] + '"
        )
    }}
}
'''

js_render_created_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    {# TODO: фильтрация по типу date/time работает в формате 2022-05-29
            решить этот момент и добавить filter = "created" #}
    return {{ result(
        caption = "data")
    }}
}
'''

js_render_last_modified_function = js_result_block + '''
function(data, type, row) {
    if (type !== 'display') {
        return data;
    }
    {# TODO: фильтрация по типу date/time работает в формате 2022-05-29
            решить этот момент и добавить filter = "last_modified" #}
    return {{ result(
        caption = "data")
    }}
}
'''

js_render_id_template = jinja2.Template(
    js_render_id_function
)

js_render_kind_template = jinja2.Template(
    js_render_kind_function
)

js_render_state_template = jinja2.Template(
    js_render_state_function
)

js_render_caption_template = jinja2.Template(
    js_render_caption_function
)

js_render_parent_template = jinja2.Template(
    js_render_parent_function
)

js_render_project_template = jinja2.Template(
    js_render_project_function
)

js_render_version_template = jinja2.Template(
    js_render_version_function
)

js_render_field_kind_template = jinja2.Template(
    js_render_field_kind_function
)

js_render_create_user_template = jinja2.Template(
    js_render_create_user_function
)

js_render_last_modify_user_template = jinja2.Template(
    js_render_last_modify_user_function
)

js_render_created_template = jinja2.Template(
    js_render_created_function
)

js_render_last_modified_template = jinja2.Template(
    js_render_last_modified_function
)

def date_to_iso(value:str):
    datetime_list = value.split()
    if not datetime_list:
        return value

    value = '-'.join(datetime_list[0].split('.')[::-1])
    if 1 == len(datetime_list):
        return value

    return f'{value} {datetime_list[1]}'